
#include "land_base.h"
#include "land_graphics.h"
#include "land_math.h"

#define LAND_2D_CORE

#include "init.h"

static inline LandFloat
crossPoint(LandVector2D* v1, LandVector2D* v2, LandVector2D* position) {
	return (v2->x - v1->x) * (position->y - v1->y)
		- (v2->y - v1->y) * (position->x - v1->x);
}

static void
updateTransform(LandObj* obj, LandMatrix2D* m) {
	if (m) {
		obj->transform = *m;
		landMoveMatrix2D(&obj->transform, &obj->position);
		landRotateMatrix2D(&obj->transform, obj->rotate);
		landScaleMatrix2D(&obj->transform, &obj->scale);
	}
	else {
		landIdentityMatrix2D(&obj->transform);
	}
	LandVector2D anchor = obj->anchor;
	LandVector2D size = obj->size;
	LandFloat left = size.x * (-anchor.x);
	LandFloat top = size.y * (-anchor.y);
	LandVector rect = {
		left, top,
		left + size.x, top + size.y,
	};
	obj->rect = rect;
}

static int
parentVisit(LandObj* obj) {
	LandMatrix2D* m = NULL;
	int force = 0;
	if (obj->parent) {
		force = parentVisit(obj->parent);
		m = &obj->parent->transform;
	}
	if (obj->dirty || force) {
		updateTransform(obj, m);
		if (obj->visit) {
			obj->visit(obj, m, obj->dirty || force);
		}
		return 1;
	}
	return 0;
}

static void
removeChild(LandObj* parent, LandObj* child) {
	if (child->parent != parent) {
		return;
	}
	for (unsigned i = child->index; i < (parent->count - 1); i++) {
		parent->children[i] = parent->children[i + 1];
	}
}

static void
removeAllChild(LandObj* obj) {
	LandObj* child;
	for (unsigned i = 0; i < obj->count; i++) {
		child = obj->children[i];
		child->parent = NULL;
		landReleaseRef(child);
	}
	obj->count = 0;
}

static void
expandChildren(LandObj* obj) {
	unsigned cap = obj->cap * 2;
	LandObj** children = malloc(sizeof(LandObj*) * cap);
	for (unsigned i = 0; i < obj->count; i++) {
		children[i] = obj->children[i];
	}
	if (obj->children != obj->childrenCache) {
		free(obj->children);
	}
	obj->cap = cap;
	obj->children = children;
}

static void
freeObj(void* ref) {
	LandObj* obj = (LandObj*)ref;
	if (obj->free) {
		obj->free(ref);
	}
	if (obj->parent) {
		removeChild(obj->parent, obj);
	}
	removeAllChild(obj);
	if (obj->children != obj->childrenCache) {
		free(obj->children);
	}
}


LAND_2D_API void*
landNewObj() {
	return landNewExtendObj(sizeof(LandObj), NULL);
}

LAND_2D_API void*
landNewExtendObj(size_t size, LandRefFree free) {
	LandObj* obj = landNewRef(size, freeObj);
	LandVector2D scale = { 1.f, 1.f, 1.f};
	LandVector2D position = { 0.f, 0.f, 1.f};
	LandVector2D anchor = { 0.f, 0.f, 1.f };
	LandVector white = { 1.f, 1.f, 1.f, 1.f };
	LandVector2D objSize = { 0.f, 0.f, 1.f };
	obj->size = objSize;
	obj->anchor = anchor;
	obj->position = position;
	obj->scale = scale;
	obj->rotate = 0.f;
	obj->children = obj->childrenCache;
	obj->cap = 8;
	obj->dirty = 1;
	obj->free = free;
	obj->visible = 1;
	obj->color = white;
	return obj;
}

LAND_2D_API void
landAddObjChild(void* p, void* c) {
	LandObj* parent = (LandObj*)p;
	LandObj* child = (LandObj*)c;
	if (child->parent) {
		return;
	}
	landRetainRef((LandRef*)child);
	child->parent = parent;
	if (parent->count >= parent->cap) {
		expandChildren(parent);
	}
	parent->children[parent->count++] = child;
}

LAND_2D_API void
landRemoveObjChild(void* p, void* c) {
	removeChild((LandObj*)p, (LandObj*)c);
	landReleaseRef(c);
}

LAND_2D_API void
landObjPosition(void* p, LandFloat x, LandFloat y) {
	LandObj* obj = (LandObj*)p;
	obj->position.x = x;
	obj->position.y = y;
	obj->dirty |= LAND_2D_OBJ_DIRTY_POSITION;
}

LAND_2D_API void
landObjRotate(void* p, LandFloat rotate) {
	LandObj* obj = (LandObj*)p;
	obj->rotate = rotate;
	obj->dirty |= LAND_2D_OBJ_DIRTY_ROTATION;
}

LAND_2D_API void
landObjScale(void* p, LandFloat x, LandFloat y) {
	LandObj* obj = (LandObj*)p;
	obj->scale.x = x;
	obj->scale.y = y;
	obj->dirty |= LAND_2D_OBJ_DIRTY_SCALE;
}


LAND_2D_API void
landObjColor(void* p, LandVector color) {
	LandObj* obj = (LandObj*)p;
	obj->color = color;
	obj->dirty |= LAND_2D_OBJ_DIRTY_COLOR;
}

LAND_2D_API void
landObjSize(void* p, LandFloat w, LandFloat h) {
	LandObj* obj = (LandObj*)p;
	obj->size.x = w;
	obj->size.y = h;
	obj->dirty |= LAND_2D_OBJ_DIRTY_SIZE;
}

LAND_2D_API void
landObjAnchor(void* p, LandFloat x, LandFloat y) {
	LandObj* obj = (LandObj*)p;
	obj->anchor.x = x;
	obj->anchor.y = y;
	obj->dirty |= LAND_2D_OBJ_DIRTY_ANCHOR;
}

LAND_2D_API void
landToObjWorld(void* p, LandVector2D position, LandVector2D* res) {
	LandObj* obj = (LandObj*)p;
	parentVisit(obj);
	landVectorMulMatrix2D(&position, &obj->transform);
	*res = position;
}

LAND_2D_API void
landToObjLocal(void* p, LandVector2D position, LandVector2D* res) {
	LandObj* obj = (LandObj*)p;
	parentVisit(obj);
	LandVector2D origin = { 0.f, 0.f, 1.f };
	landVectorMulMatrix2D(&origin, &obj->transform);
	landSubVector2D(&position, &origin);
	LandFloat rotate = -obj->rotate;
	LandVector2D scale = { 1.f / obj->scale.x, 1.f / obj->scale.y, 1.f };
	LandObj* parent = obj->parent;
	while (parent) {
		rotate -= parent->rotate;
		scale.x /= parent->scale.x;
		scale.y /= parent->scale.y;
		parent = parent->parent;
	}
	LandMatrix2D m;
	landIdentityMatrix2D(&m);
	landScaleMatrix2D(&m, &scale);
	landRotateMatrix2D(&m, rotate);
	landVectorMulMatrix2D(&position, &m);
	*res = position;
}

LAND_2D_API void
landObjEvent(void* p, LandObjEvent* evt) {
	LandObj* obj = (LandObj*)p;
	if (!obj->visible) {
		return;
	}
	LandObj* child;
	for (unsigned i = obj->count; i > 0; i--) {
		child = obj->children[i - 1];
		landObjEvent(child, evt);
		if (evt->intercept) {
			return;
		}
	}
	if (!evt->intercept && obj->onEvent) {
		obj->onEvent(obj, evt);
	}
}

LAND_2D_API int
landObjHit(void* p, LandFloat x, LandFloat y) {
	LandObj* obj = (LandObj*)p;
	parentVisit(obj);
	LandVector* rect = &obj->rect;
	LandVector2D points[] = {
		{rect->left, rect->top, 1.f},
		{rect->left, rect->bottom, 1.f},
		{rect->right, rect->top, 1.f},
		{rect->right, rect->bottom, 1.f},
	};
	for (unsigned i = 0; i < 4; i++) {
		landVectorMulMatrix2D(&points[i], &obj->transform);
	}
	LandVector2D position = { x, y, 1.f };
	LandFloat f = crossPoint(&points[0], &points[1], &position);
	f *= crossPoint(&points[3], &points[2], &position);
	if (f < 0.f) {
		return 0;
	}
	f = crossPoint(&points[1], &points[3], &position);
	f *= crossPoint(&points[2], &points[0], &position);
	return (f >= 0.f);
}

void
landVisitObj(void* p, LandMatrix2D* transform, int force) {
	LandObj* obj = (LandObj*)p;
	if (!obj->visible) {
		return;
	}
	if (obj->dirty || force) {
		updateTransform(obj, transform);
		if (obj->visit) {
			obj->visit(obj, transform, force);
		}
	}
	LandObj* child;
	for (unsigned i = 0; i < obj->count; i++) {
		child = obj->children[i];
		landVisitObj(child, &obj->transform, obj->dirty || force);
	}
	obj->dirty = 0;
}

void
landDrawObj(LandScene* scene, LandObj* obj) {
	if (obj->draw) {
		obj->draw(scene, obj);
	}
	LandObj* child;
	for (unsigned i = 0; i < obj->count; i++) {
		child = obj->children[i];
		landDrawObj(scene, child);
	}
}

void
landPreDrawObj(LandScene* scene, LandObj* obj) {
	if (obj->preDraw) {
		obj->preDraw(scene, obj);
	}
	LandObj* child;
	for (unsigned i = 0; i < obj->count; i++) {
		child = obj->children[i];
		landPreDrawObj(scene, child);
	}
}

unsigned
landPushObjRect(LandVertex* vertex,
	LandVector* rect, LandVector* uv, LandVector* color,
	unsigned* offset, LandMatrix2D* transform) {
	LandPoint points[] = {
		{
			0.f, 0.f, 0.f, 1.f,
			uv->left, uv->top, 0.f, 0.f,
			color->r, color->g, color->b, color->a,
			0.f, 0.f, 1.f, 1.f,
		},{
			0.f, 0.f, 0.f, 1.f,
			uv->left, uv->bottom, 0.f, 0.f,
			color->r, color->g, color->b, color->a,
			0.f, 0.f, 1.f, 1.f,
		},{
			0.f, 0.f, 0.f, 1.f,
			uv->right, uv->top, 0.f, 0.f,
			color->r, color->g, color->b, color->a,
			0.f, 0.f, 1.f, 1.f,
		},{
			0.f, 0.f, 0.f, 1.f,
			uv->right, uv->bottom, 0.f, 0.f,
			color->r, color->g, color->b, color->a,
			0.f, 0.f, 1.f, 1.f,
		},
	};
	LandVector2D ps[] = {
		{rect->left, rect->top, 1.f},
		{rect->left, rect->bottom, 1.f},
		{rect->right, rect->top, 1.f},
		{rect->right, rect->bottom, 1.f},
	};
	for (unsigned i = 0; i < 4; i++) {
		if (transform) {
			landVectorMulMatrix2D(&ps[i], transform);
		}
		points[i].position.x = ps[i].x;
		points[i].position.y = ps[i].y;
	}
	unsigned indexs[] = { 0, 1, 2, 1, 2, 3 };
	if (offset) {
		for (unsigned i = 0; i < 6; i++) {
			indexs[i] += *offset;
		}
		*offset += 4;
	}
	landPushList(vertex->points, points, 4);
	landPushList(vertex->indexs, indexs, 6);
	return 6;
}